#pragma once
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <memory>
#include "myh/MyLog.hpp"

class Socket
{
static const std::string defaultip;
static const int backlog = 5;
static const int SIZE = 4096;

    void HandleError(const std::string& name,int _erron)
    {
        if(_erron < 0)
        {
            lg.defaultprint(name,errno);
        }
    }
    typedef std::unique_ptr<sockaddr_in> Sockdddr_Ptr;

public:
    Socket(uint16_t port, const std::string &ip = defaultip, int domain = AF_INET, int type = SOCK_STREAM)
        :_port(port),_ip(ip),_domain(domain),_type(type),_addr(std::make_unique<sockaddr_in>())
    {
        _sockfd = socket(_domain,_type,0);
        HandleError("socket",_sockfd);


        _addr->sin_family = _domain;
        _addr->sin_port = htons(_port);
        inet_pton(_domain,ip.c_str(),&(_addr->sin_addr));
        _len = sizeof(sockaddr_in);
    }

    void Bind()
    {
        int ret = bind(_sockfd,(const sockaddr *)_addr.get(),_len); 
        HandleError("bind",ret);
    }

    void Listen()
    {
        int ret = listen(_sockfd,backlog);
        HandleError("listen",ret);
    }

    int Accept(sockaddr_in* addr)
    {
        socklen_t len = sizeof(&addr);
        int listensockfd = accept(_sockfd,(sockaddr*)(addr),&len);
        HandleError("accept",listensockfd);
        return listensockfd;
    }

    int Connect()
    {
        int ret = connect(_sockfd,(const sockaddr *)(_addr.get()),_len);
        HandleError("connect",ret);
        return ret;
    }

    std::string Recvform(sockaddr_in * addr)
    {
        socklen_t len = sizeof(&addr);
        char buffer[SIZE];
        size_t ret = recvfrom(_sockfd,buffer,SIZE,0,(sockaddr*)addr,&len);
        HandleError("recvfrom",ret);
        buffer[ret] = 0;
        return buffer;
    }

    void Sendto(std::string s)
    {
        int ret = sendto(_sockfd,s.c_str(),s.size(),0,(const sockaddr *)(_addr.get()),_len);
        HandleError("sendto",ret);
    }
    int GetSockfd() const {return _sockfd;}
    void Close() const {close(_sockfd);}
private:
    //M
    uint16_t _port;
    std::string _ip;
    int _domain;
    int _type;
    Sockdddr_Ptr _addr;
    int _sockfd;
    socklen_t _len;
};
const std::string Socket::defaultip = "0.0.0.0";